// 1 使用call调用函数
let object = { id: 1, name: 'tom' }

function fn(a, b) {
  console.log(this) // object:{id: 1, name: 'tom'}
  console.log(this.name) // tom
  console.log(a + b) // 7
}
// window.fn() // window对象调用fn函数，window是全局对象，一般情况可以省略fn()

console.log(this === window) // true
// 此时可以使用object调用fn吗?
// fn.call(this) // this通过call方法调用fn函数
// fn.call(object, 1, 2) // object通过call方法调用fn函数,传递参数1，2

// 2 使用apply调用函数
// fn.apply(object, [3, 4]) // object通过apply方法调用fn函数,传递参数3，4

// 3 使用bind调用函数
let rt = fn.bind(object, 10, 20) // object通过bind方法调用fn函数,传递参数10，20,获得一个返回值rt,rt是函数，执行rt函数，才会调用fn函数
rt()

/*
结论:
  1 全局作用域中的this是window对象
  2 函数中的this是函数的调用对象
  3 可以通过call,apply,bind为函数指定不同的调用者，即使函数和调用者没有任何关系
  4 call,apply,bind区别
    4-1 都得调用函数并指定this的方法
    4-2 call参数使用逗号隔开
    4-3 apply是数组传递参数
    4-4 bind参数和call相同,但不是立即执行fn函数，而是返回一个函数rt，必须手动调用rt才会执行函数fn
*/

// 5 函数本身也是引用数据类型类似对象，也有属性和方法
function func(a, b, c) {
  console.log(arguments) // [1,2,3]
  return a + b + c + arguments[3]
}
// 5-1 函数的名称 fn.name
console.log(func.name) // func
// 5-2 函数的长度 fn.length
console.log(func.length) // 3
// 5-3 函数的默认参数 arguments,箭头函数没有arguments
console.log(func(1, 2, 3, 4)) // 6
// 此函数没有参数列表，但有默认参数数组
function f() {
  return arguments[0] + arguments[1]
}
rt = f('hello', ' world')
console.log(rt) // hello world
// 5-4 函数属性prototype,称原型对象
let proto = func.prototype
console.log(proto) // {constructor: ƒ}
// 5-5 函数参数的默认值 fn(a=10,b=1,c=2)
let f1 = (a = 10, b = 20, c = 30) => {
  return a + b + c
}
// 没有传递任何实际参数
console.log(f1(1, 2, 3)) // 6
// 5-6 函数参数的剩余参数 fn(...arr)
let f2 = (...arr) => {
  return arr.reduce((p, n) => p + n)
}
rt = f2(1, 2, 3, 4, 5)
console.log(rt) // 15
